package org.store.dao;


import org.springframework.dao.DataAccessException;
import org.springframework.jdbc.core.RowMapper;
import org.springframework.transaction.annotation.Transactional;
import org.store.domain.Address;
import org.store.domain.Buyer;

import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.List;

public class BuyerDao extends AbstractDao<Buyer> {

    private AddressDao addressDao;

    @Override
    public Buyer getById(long id) throws DaoException {
        try {
            return jdbcTemplate.queryForObject("select b.buyer_id, b.name, b.email, a.address_id, a.street1, a.street2, a.city, a.country, a.zipCode, a.phone " +
                    "from buyer b join address a on (b.address_id = a.address_id) where b.buyer_id = ?", MAPPER, id);
        } catch (DataAccessException e) {
            throw new DaoException("Cannot get this buyer with id = " + id, e);
        }
    }

    @Override
    @Transactional
    public Buyer save(Buyer buyer) throws DaoException {
        if (buyer == null)
            throw new IllegalArgumentException("Buyer cannot be null");
        Address addr = buyer.getShipmentAddress();
        if (addr != null && addr.getId() == null) {
            buyer = new Buyer(buyer.getName(), buyer.getEmail(), addressDao.save(addr));
        }
        try {
            long id = jdbcTemplate.queryForLong("insert into buyer (name, email, address_id) values(?,?,?) returning buyer_id",
                    buyer.getName(), buyer.getEmail(), addr == null ? null : addr.getId());
            buyer.setId(id);
            return buyer;
        } catch (DataAccessException e) {
            throw new DaoException("Cannot save buyer with id = " + buyer.getId(), e);
        }
    }

    @Override
    public boolean delete(Buyer buyer) throws DaoException {
        try {
            int updated = jdbcTemplate.update("delete from buyer where buyer_id = ?", buyer.getId());
            return updated > 0;
        } catch (DataAccessException e) {
            throw new DaoException("Cannot delete buyer with id = " + buyer.getId(), e);
        }
    }

    @Override
    public Buyer saveOrUpdate(Buyer buyer) throws DaoException {
        if (buyer.getId() == null) {
            return save(buyer);
        }
        try {
            jdbcTemplate.update("update buyer SET name = ?, email = ? where buyer_id = ?", buyer.getName(),
                    buyer.getEmail(), buyer.getId());
            return buyer;
        } catch (DataAccessException e) {
            throw new DaoException("Cannot update buyer with id = " + buyer.getId(), e);
        }
    }

    @Override
    @Transactional
    public List<Buyer> saveAll(final List<Buyer> buyers) throws DaoException {
        if (buyers == null)
            throw new IllegalArgumentException("Array cannot be null");
        for (Buyer buyer : buyers) {
            save(buyer);
        }
        return buyers;
    }

    @Override
    public boolean isExists(long id) throws DaoException {
        try {
            return jdbcTemplate.queryForLong("select count(*) from buyer where buyer_id = ?", id) > 0;
        } catch (DataAccessException e) {
            throw new DaoException("Cannot get buyer with id = " + id, e);
        }
    }

    private static final RowMapper<Buyer> MAPPER = new RowMapper<Buyer>() {
        public Buyer mapRow(ResultSet rs, int rowNum) throws SQLException {
            Address addr = new Address(
                    rs.getString("street1"),
                    rs.getString("street2"),
                    rs.getString("city"),
                    rs.getString("country"),
                    rs.getString("zipCode"),
                    rs.getString("phone")
            );
            addr.setId(rs.getLong("address_id"));
            Buyer buyer = new Buyer(
                    rs.getString("name"),
                    rs.getString("email"),
                    addr
            );
            buyer.setId(rs.getLong("buyer_id"));
            return buyer;
        }
    };

    public void setAddressDao(AddressDao addressDao) {
        this.addressDao = addressDao;
    }
}
